package com.jvxb.manage.livable.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jvxb.common.constants.DbConstants;
import com.jvxb.common.constants.WorkOrderConstants;
import com.jvxb.common.utils.CommonUtil;
import com.jvxb.manage.configuration.websocket.WebSocketServer;
import com.jvxb.manage.livable.entity.WorkOrder;
import com.jvxb.manage.livable.mapper.WorkOrderMapper;
import com.jvxb.manage.livable.service.DataAlertService;
import com.jvxb.manage.livable.service.DbDeployService;
import com.jvxb.manage.livable.service.WorkOrderService;
import com.jvxb.manage.livable.service.dataAlert.MySQLDataAlertService;
import com.jvxb.manage.livable.service.dataAlert.RedisDataAlertService;
import com.jvxb.manage.livable.util.WorkOrderUtil;
import com.jvxb.manage.livable.vo.Employee;
import com.jvxb.manage.livable.vo.WorkOrderOperateVo;
import com.jvxb.modules.configuration.exception.ServiceException;
import com.jvxb.modules.configuration.security.SecurityUtil;
import com.jvxb.modules.configuration.security.UserDetail;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author jvxb
 * @since 2022-01-07
 */
@Log4j2
@Service
public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {

    @Autowired
    DbDeployService dbDeployService;
    @Autowired
    @Lazy
    DataAlertService dataAlertService;

    @Override
    public boolean save(WorkOrder workOrder) {
        Integer result = baseMapper.insert(workOrder);
        boolean success = null != result && result >= 1;
        //新增工单成功，通知审批人
        if (success && workOrder.getApprovers() != null) {
            informUsers(WorkOrderUtil.getUserCodeList(workOrder.getApprovers()));
        }
        return success;
    }

    @Override
    public void approve(WorkOrderOperateVo operateVo) {
        //1）判断当前用户能否审批、是否已经审批。
        Long currentUserId = SecurityUtil.currentUserId();
        WorkOrder workOrder = baseMapper.selectById(operateVo.getId());
        if (!workOrder.getOrderStatus().equals(WorkOrderConstants.CREATE)) {
            throw new ServiceException("该工单已被审批，请勿重复操作。工单id=" + operateVo.getId());
        }
        int checkAuthResult = checkApproveAuth(workOrder, operateVo, currentUserId);
        if (checkAuthResult < 0) {
            throw new ServiceException("您无法审批该工单，请勿操作。工单id=" + operateVo.getId());
        }
        //2）如果是最后一个审批人审批通过，则工单状态变为已审批，且指定下一个处理人为执行人
        if (operateVo.getStatus().equals(WorkOrderConstants.APPROVAL_SUCCESS) && checkAuthResult == 1) {
            workOrder.setOrderStatus(WorkOrderConstants.APPROVAL_SUCCESS);
            workOrder.setAssignee(workOrder.getOperator());
            //通知执行人
            informUser(workOrder.getExecuteId().toString());
        } else {
            workOrder.setOrderStatus(operateVo.getStatus());
        }
        workOrder.setCheckTime(new Date());
        baseMapper.updateById(workOrder);

        //3）如果审批通过后需要回调，则回调
        if (operateVo.getStatus().equals(WorkOrderConstants.APPROVAL_SUCCESS) && checkAuthResult == 1
                && CommonUtil.notNullOrEmpty(workOrder.getApproveCallback())) {
            try {
                HttpUtil.post(workOrder.getApproveCallback(), JSON.toJSONString(operateVo));
            } catch (Exception e) {
                log.error("工单审批通过回调异常，工单id=" + operateVo.getId(), e);
            }
        }
    }

    /**
     * 工单通用操作：审批通过/驳回，执行终止/执行驳回、签收。
     * 点击执行时，先调用的是：executeWorkOrder 方法
     */
    @Override
    public void operate(WorkOrderOperateVo operateVo) throws Exception {
        log.info("operate: " + operateVo);
        if (operateVo.getExecuteType().equalsIgnoreCase(WorkOrderConstants.HANDLE_APPROVAL)) {
            approve(operateVo);
        } else if (operateVo.getExecuteType().equalsIgnoreCase(WorkOrderConstants.HANDLE_EXECUTE)) {
            execute(operateVo);
        } else if (operateVo.getExecuteType().equalsIgnoreCase(WorkOrderConstants.HANDLE_SIGN)) {
            sign(operateVo);
        }
    }

    @Override
    public void executeSuccess(WorkOrderOperateVo operateVo) {
        execute(operateVo);
    }

    @Override
    public void executeFail(Long workOrderId, String errMsg) {
        //更新执行状态
        WorkOrder workOrder = getOne(Condition.<WorkOrder>create().eq(WorkOrder.ID, workOrderId).select(WorkOrder.OPERATOR, WorkOrder.EXECUTE_ID));
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq(WorkOrder.ID, workOrderId);
        updateWrapper.set(WorkOrder.ORDER_STATUS, WorkOrderConstants.EXECUTE_FAILURE);
        updateWrapper.set(WorkOrder.ASSIGNEE, workOrder.getOperator());
        updateWrapper.set(WorkOrder.ERR_MSG, errMsg);
        update(workOrder, updateWrapper);
        //通知创建人
        informUser(workOrder.getExecuteId().toString());
    }

    @Override
    public void informUser(String userCode) {
        try {
            Set<String> onlineUserIds = WebSocketServer.webSocketMap.keySet();
            if (onlineUserIds.contains(userCode)) {
                WebSocketServer.sendInfo(JSON.toJSONString(getInformMsgMap(userCode)), userCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("推送用户[" + userCode + "]待办消息异常：" + e.getMessage());
        }
    }

    @Override
    public void informUsers(List<String> userCodeList) {
        try {
            Set<String> onlineUserIds = WebSocketServer.webSocketMap.keySet();
            for (String userCode : userCodeList) {
                if (onlineUserIds.contains(userCode)) {
                    WebSocketServer.sendInfo(JSON.toJSONString(getInformMsgMap(userCode)), userCode);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Map getInformMsgMap(String userId) {
        int systemInformCount = 0;
        int workOrderCount = count(Condition.<WorkOrder>create().like(WorkOrder.ASSIGNEE, String.format("\"userCode\":\"%s\"", userId)));
        Map infoMsgMap = new HashMap();
        infoMsgMap.put("workOrderCount", workOrderCount);
        infoMsgMap.put("systemInformCount", systemInformCount);
        //总共统计：工单、系统通知...
        infoMsgMap.put("totalMsgCount", workOrderCount + systemInformCount);
        return infoMsgMap;
    }

    @Override
    public void executeWorkOrder(WorkOrderOperateVo operateVo) {
        //工单在执行中，不再执行
        WorkOrder workOrder = getOne(Condition.<WorkOrder>create().eq(WorkOrder.ID, operateVo.getId())
                .select(WorkOrder.ID, WorkOrder.ORDER_STATUS, WorkOrder.PROCESS_TYPE, WorkOrder.DB_TYPE, WorkOrder.OPERATOR));
        if (WorkOrderConstants.RUNNING.equals(workOrder.getOrderStatus())) {
            throw new ServiceException("该工单已被执行，请勿操作。工单id=" + operateVo.getId());
        }
        //寻找工单的执行方法。
        boolean findExecutor = findOrderExecutor(workOrder, operateVo);
        //无执行方法的工单提示
        if (!findExecutor) {
            throw new ServiceException("工单未指定执行方法，请联系平台值班查看");
        }
        //如果存在工单执行方法，更新工单状态为执行中
        updateWorkOrderStatuToRunning(workOrder);
    }

    private void updateWorkOrderStatuToRunning(WorkOrder workOrder) {
        UserDetail currentUser = SecurityUtil.currentUser();
        Employee currentEmp = new Employee(currentUser.getUserCode(), currentUser.getUserName(), currentUser.getDepartmentName());
        Employee operator = JSONObject.parseObject(workOrder.getOperator(), Employee.class);
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq(WorkOrder.ID, workOrder.getId());
        updateWrapper.set(WorkOrder.ORDER_STATUS, WorkOrderConstants.RUNNING);
        updateWrapper.set(WorkOrder.ASSIGNEE, null);
        updateWrapper.set(WorkOrder.EXECUTE_TIME, new Date());
        if (!operator.getUserCode().equals(currentUser.getUserCode())) {
            updateWrapper.set(WorkOrder.OPERATOR, JSON.toJSONString(currentEmp));
            updateWrapper.set(WorkOrder.EXECUTE_ID, currentEmp.getUserCode());
        }
        update(workOrder, updateWrapper);
    }

    private boolean findOrderExecutor(WorkOrder workOrder, WorkOrderOperateVo operateVo) {
        //调用实际的执行方法
        if (workOrder.getProcessType().equals(WorkOrderConstants.DB_DEPLOY)) {
            dbDeployService.executeOrder(operateVo);
            return true;
        } else if (workOrder.getProcessType().equals(WorkOrderConstants.DB_DATA_ALERT)) {
            dataAlertService.executeOrder(operateVo);
            return true;
        }
        return false;
    }

    private void execute(WorkOrderOperateVo operateVo) {
        //查找工单的执行人
        WorkOrder workOrder = getOne(Condition.<WorkOrder>create().eq(WorkOrder.ID, operateVo.getId())
                .select(WorkOrder.OPERATOR, WorkOrder.CREATOR, WorkOrder.CREATER_ID, WorkOrder.ORDER_STATUS));
        //正在执行中的工单，不允许驳回或终止。
        if (WorkOrderConstants.RUNNING.equals(workOrder.getOrderStatus())
                && (operateVo.getStatus().equals(WorkOrderConstants.OPERATE_REFUSE) || operateVo.getStatus().equals(WorkOrderConstants.OPERATE_FINISH))) {
            throw new ServiceException("该工单已被执行，请勿操作。工单id=" + operateVo.getId());
        }
        Date date = new Date();
        //执行人已执行
        Employee operator = JSONObject.parseObject(workOrder.getOperator(), Employee.class);
        operator.setDesc(operateVo.getDesc());
        operator.setStatus(operateVo.getStatus());
        //更新执行状态
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq(WorkOrder.ID, operateVo.getId());
        updateWrapper.set(WorkOrder.OPERATOR, JSON.toJSONString(operator));
        //执行驳回
        if (operateVo.getStatus().equals(WorkOrderConstants.OPERATE_REFUSE)) {
            updateWrapper.set(WorkOrder.ORDER_STATUS, WorkOrderConstants.EXECUTE_REFUSE);
            updateWrapper.set(WorkOrder.ASSIGNEE, workOrder.getCreator());
            updateWrapper.set(WorkOrder.EXECUTE_TIME, date);
            updateWrapper.set(WorkOrder.EXECUTE_END_TIME, date);
        }
        //执行终止
        else if (operateVo.getStatus().equals(WorkOrderConstants.OPERATE_FINISH)) {
            updateWrapper.set(WorkOrder.ORDER_STATUS, WorkOrderConstants.EXECUTE_STOP);
            updateWrapper.set(WorkOrder.ASSIGNEE, workOrder.getCreator());
            updateWrapper.set(WorkOrder.EXECUTE_TIME, date);
            updateWrapper.set(WorkOrder.EXECUTE_END_TIME, date);
        }
        //执行成功
        else {
            updateWrapper.set(WorkOrder.ORDER_STATUS, WorkOrderConstants.EXECUTE_SUCCESS);
            updateWrapper.set(WorkOrder.ASSIGNEE, workOrder.getCreator());
            updateWrapper.set(WorkOrder.EXECUTE_END_TIME, date);
            //通知创建人
            informUser(workOrder.getCreaterId().toString());
        }
        update(workOrder, updateWrapper);
    }

    /**
     * 检查审批权限
     *
     * @param workOrder
     * @param operateVo
     * @param currentUserId
     * @return
     */
    private int checkApproveAuth(WorkOrder workOrder, WorkOrderOperateVo operateVo, Long currentUserId) {
        List<Employee> employees = JSONArray.parseArray(workOrder.getApprovers(), Employee.class);
        boolean canApprove = false;
        //当前工单未审批数
        int unApproveCount = 0;
        //记录用户能否审批工单与未审批数
        for (Employee employee : employees) {
            if (employee.getStatus() == null) {
                unApproveCount++;
            }
            if (employee.getUserCode().equals(currentUserId.toString())) {
                if (WorkOrderConstants.APPROVAL_SUCCESS.equals(employee.getStatus())) {
                    throw new ServiceException("该工单已经审批，请勿重复操作。工单id=" + operateVo.getId());
                }
                canApprove = true;
                employee.setStatus(operateVo.getStatus());
                employee.setDesc(operateVo.getDesc());
                workOrder.setApprovers(JSON.toJSONString(employees));
            }
        }

        if (!canApprove) {
            return -1;
        }
        return unApproveCount;
    }

    private void sign(WorkOrderOperateVo operateVo) {
        //查找工单的创建人
        WorkOrder workOrder = getOne(Condition.<WorkOrder>create().eq(WorkOrder.ID, operateVo.getId()).select(WorkOrder.CREATOR));
        Employee creator = JSONObject.parseObject(workOrder.getCreator(), Employee.class);
        if (!creator.getUserCode().equals(SecurityUtil.currentUserId().toString())) {
            throw new RuntimeException("您无权签收该工单，请联系平台查看。");
        }
        creator.setStatus(operateVo.getStatus());
        creator.setDesc(operateVo.getDesc());
        //更新执行状态
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq(WorkOrder.ID, operateVo.getId());
        updateWrapper.set(WorkOrder.CREATOR, JSON.toJSONString(creator));
        updateWrapper.set(WorkOrder.ASSIGNEE, null);
        updateWrapper.set(WorkOrder.SIGN_TIME, new Date());
        update(workOrder, updateWrapper);
    }
}
